Inside Macintosh: Memory

Previous | Chapter Top | Chapter Contents | Next

Installing a Purge-Warning Procedure

You can define a purge-warning procedure that the Memory Manager calls whenever it is about to purge a block from your application heap. You can use this procedure to save the data in the block, if necessary, or to perform other processing in response to this notification.

Most applications don't need to install a purge-warning procedure. This capability is provided primarily for applications that require greater control over their heap. Examples are applications that maintain purgeable handles containing important data and applications that for any other reason need notification when a block is about to be purged.

When your purge-warning procedure is called, the Memory Manager passes it a handle to the block about to be purged. In your procedure, you can test the handle to determine whether it contains data that needs to be saved; if so, you can save the data (possibly by writing it to some open file). Listing 6 defines a very simple purge-warning procedure.

Listing 6 A purge-warning procedure

PROCEDURE MyPurgeProc (h: Handle);
VAR
    theA5:      LongInt;                    {value of A5 when procedure is called}
BEGIN
    theA5 := SetCurrentA5;                  {remember current value of A5; install ours}
    IF BAND(HGetState(h), $20) = 0 THEN
        BEGIN                               {if the handle isn't a resource handle}
            IF InSaveList(h) THEN
                WriteData(h);               {save the data in the block}
        END;
    theA5 := SetA5(theA5);                  {restore previous value of A5}
END;

The MyPurgeProc procedure defined in Listing 6 inspects the handle's properties (using HGetState ) to see whether its resource bit is clear. If so, the procedure next determines whether the handle is contained in an application-maintained list of handles whose data should be saved before purging. If the handle is in that list, the purge-warning procedure writes its data to disk. (The file into which the data is written should already be open at the time the procedure is called, because opening a file might cause memory to move.)

Note that MyPurgeProc sets up the A5 register with the application's A5 value upon entry and restores it to its previous value before exiting. This is necessary because you cannot rely on the A5 register within a purge-warning procedure.

Because of the optimizations performed by some compilers, the actual work of the purge-warning procedure and the setting and restoring of the A5 register might have to be placed in separate procedures. See the chapter "Vertical Retrace Manager" in Inside Macintosh: Processes for an illustration of how you can do this.<36pt>

To install a purge-warning procedure, you need to install the address of the procedure into the purgeProc field of your application's heap zone header. Listing 7 illustrates one way to do this.

Listing 7 Installing a purge-warning procedure

PROCEDURE InstallPurgeProc;
VAR
    myZone:     THz;
BEGIN
    myZone := GetZone;                                  {find the current zone header}
    gPrevProc := myZone^.purgeProc;                     {remember previous procedure}
    myZone^.purgeProc := @MyPurgeProc;                  {install new procedure}
END;

The InstallPurgeProc procedure defined in Listing 7 first obtains the address of the current heap zone by calling the GetZone function. Then it saves the address of any existing purge-warning procedure in the global variable gPrevProc. Finally, InstallPurgeProc installs the new procedure by putting its address directly into the purgeProc field of the zone header. (For more information on zone headers, see "Heap Zones" .)

Keep in mind that the Memory Manager calls your purge-warning procedure each time it decides to purge any purgeable block, and it might call your procedure far more often than you would expect. Your purge-warning procedure might be passed handles not only to blocks that you explicitly mark as purgeable (by calling HPurge ), but also to resources whose purgeable attribute is set. (In general, applications don't need to take any action on handles that belong to the Resource Manager.) Because of the potentially large number of times your purge-warning procedure might be called, it should be able to determine quickly whether a handle that is about to be purged needs additional processing.

Remember that a purge-warning procedure is called during the execution of some Memory Manager routine. As a result, your procedure cannot cause memory to be moved or purged. In addition, it should not dispose of the handle it is passed or change the purge status of the handle. See "Purge-Warning Procedures" for a complete description of the limitations on purge-warning procedures.

If your application calls the Resource Manager procedure SetResPurge with the parameter TRUE (to have the Resource Manager automatically save any modified resources that are about to be purged), you should avoid using a purge-warning procedure. This is because the Resource Manager installs its own purge-warning procedure when you call SetResPurge in this way. If you must install your own purge-warning procedure, you should remove your procedure, call SetResPurge , then reinstall your procedure as shown in Listing 7 . You then need to make sure that your procedure calls the Resource Manager's purge-warning procedure (which is saved in the global variable gPrevProc ) before exiting. Most applications do not need to call SetResPurge at all.<36pt>

If your application does call SetResPurge(TRUE) , you should use the version of MyPurgeProc defined in Listing 8 . It is just like the version defined in Listing 6 except that it calls the Resource Manager's purge-warning procedure before exiting.

Listing 8 A purge-warning procedure that calls the Resource Manager's procedure

PROCEDURE MyPurgeProc (h: Handle);
VAR
    theA5:      LongInt;                    {value of A5 when procedure is called}
BEGIN
    theA5 := SetCurrentA5;                  {remember current value of A5; install ours}
    IF BAND(HGetState(h), $20) = 0 THEN
        BEGIN                               {if the handle isn't a resource handle}
            IF InSaveList(h) THEN
                WriteData(h);               {save the data in the block}
        END
    ELSE IF gPrevProc <> NIL THEN
        CallByAddress(gPrevProc);
    theA5 := SetA5(theA5);                  {restore previous value of A5}
END;

See Listing 4 on Calling a procedure by address for a definition of the procedure CallByAddress .


© 1997 Apple Computer, Inc.

Previous | Chapter Top | Chapter Contents | Next